{
    "cells": [
     {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
       "import os\n",
       "import requests\n",
       "import json\n",
       "import pandas as pd\n",
       "\n",
       "import plotly.express as px\n",
       "import plotly.graph_objects as go\n",
       "\n",
       "import ipywidgets as widgets\n",
       "from IPython.display import display, HTML\n",
       "from IPython.display import clear_output"
      ]
     },
     {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
       "GITHUB_TOKEN = os.getenv('GITHUB_TOKEN')\n",
       "GITHUB_TOKEN = input(\"Enter Github Token\") if GITHUB_TOKEN is None else GITHUB_TOKEN\n",
       "os.environ['GITHUB_TOKEN'] = GITHUB_TOKEN\n",
       "if GITHUB_TOKEN is None or GITHUB_TOKEN == '':\n",
       "    raise ValueError(\"Please set your GITHUB_TOKEN\")\n",
       "\n",
       "REPO = 'microsoft/vscode-jupyter'  # Replace with your repository\n",
       "\n",
       "pd.set_option('display.max_rows', 100)\n",
       "pd.set_option('display.min_rows', 100)\n",
       "pd.set_option(\"display.max_rows\", 100, \"display.max_columns\", 100)\n"
      ]
     },
     {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
       "url = 'https://api.github.com/graphql'\n",
       "headers = {'Authorization': f'bearer {GITHUB_TOKEN}'}\n",
       "\n",
       "query = '''\n",
       "query ($repo_owner: String!, $repo_name: String!, $cursor: String) {\n",
       "  repository(owner: $repo_owner, name: $repo_name) {\n",
       "    issues(first: 100, after: $cursor, states: OPEN) {\n",
       "      pageInfo {\n",
       "        endCursor\n",
       "        hasNextPage\n",
       "      }\n",
       "      nodes {\n",
       "        number\n",
       "        title\n",
       "        createdAt\n",
       "        author {\n",
       "          avatarUrl\n",
       "          login\n",
       "        }\n",
       "        reactions{\n",
       "          totalCount\n",
       "        }\n",
       "        labels(first: 10) {\n",
       "          nodes {\n",
       "            name\n",
       "          }\n",
       "        }\n",
       "        assignees(first: 4) {\n",
       "          nodes {\n",
       "            avatarUrl\n",
       "            login\n",
       "          }\n",
       "        }\n",
       "        comments(last:1) {\n",
       "          nodes {\n",
       "            author {\n",
       "              avatarUrl\n",
       "              login\n",
       "            }\n",
       "            createdAt\n",
       "            lastEditedAt\n",
       "          }\n",
       "        }\n",
       "      }\n",
       "    }\n",
       "  }\n",
       "}'''\n",
       "\n",
       "variables = {\n",
       "    'repo_owner': REPO.split('/')[0],\n",
       "    'repo_name': REPO.split('/')[1],\n",
       "    'cursor': None\n",
       "}\n",
       "\n",
       "all_issues = []\n",
       "\n",
       "while True:\n",
       "    response = requests.post(url, headers=headers, json={'query': query, 'variables': variables})\n",
       "    data = response.json()\n",
       "    issues = data['data']['repository']['issues']['nodes']\n",
       "    all_issues.extend(issues)\n",
       "    page_info = data['data']['repository']['issues']['pageInfo']\n",
       "    if not page_info['hasNextPage']:\n",
       "        break\n",
       "    variables['cursor'] = page_info['endCursor']"
      ]
     },
     {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
       "def get_issue_type(labels):\n",
       "    if 'bug' in labels:\n",
       "        return 'bug'\n",
       "    elif 'feature-request' in labels:\n",
       "        return 'feature-request'\n",
       "    elif 'debt' in labels:\n",
       "        return 'debt'\n",
       "    else:\n",
       "        return 'other'\n",
       "\n",
       "issue_details = []\n",
       "for issue in all_issues:\n",
       "    try:\n",
       "        author = 'ghost' if issue['author'] is None else issue['author']['login']\n",
       "        authorAvatarUrl = 'https://avatars.githubusercontent.com/u/10137?v=4' if issue['author'] is None else issue['author']['avatarUrl']\n",
       "        issue_details.append({\n",
       "          'id': f'<a href=\"http://github.com/{REPO}/issues/{issue['number']}\">{issue['number']}</a>',\n",
       "          'reactions': issue['reactions']['totalCount'],\n",
       "          'title': issue['title'],\n",
       "          '_': f'<a href=\"https://github.com/{author}\"><img height=\"20\" width=\"20\" src=\"{authorAvatarUrl}\"></a>',\n",
       "          'Author':  f'<a href=\"https://github.com/{author}\">{author}</a>',\n",
       "          'labels': ', '.join([label['name'] for label in issue['labels']['nodes']]),\n",
       "          'Created': pd.to_datetime(issue['createdAt']).strftime(\"%Y-%m-%d\"),\n",
       "          'Updated': None if len(issue['comments']['nodes']) == 0 else pd.to_datetime(issue['comments']['nodes'][0]['createdAt']).strftime(\"%Y-%m-%d\"),\n",
       "          'AssignedTo': ' '.join([f'<a href=\"https://github.com/{assignee['avatarUrl']}\"><img height=\"20\" width=\"20\" src=\"{assignee['avatarUrl']}\"></a>' for assignee in issue['assignees']['nodes']]),\n",
       "          'type': get_issue_type([label['name'] for label in issue['labels']['nodes']]),\n",
       "          'assignees': ', '.join([assignee['login'] for assignee in issue['assignees']['nodes']]),\n",
       "          'labels_list': list([label['name'] for label in issue['labels']['nodes']])\n",
       "        })\n",
       "    except Exception as e:\n",
       "        print(issue)\n",
       "        raise e\n",
       "        break\n",
       "\n",
       "print(f\"Total issues: {len(issue_details)}\")\n",
       "\n",
       "df = pd.DataFrame(issue_details)\n"
      ]
     },
     {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
       "from itables import init_notebook_mode, show\n",
       "import itables.options as opt\n",
       "\n",
       "\n",
       "init_notebook_mode(all_interactive=True)\n",
       "opt.showIndex = False\n",
       "opt.keys = True\n",
       "opt.column_filters = \"footer\"\n",
       "opt.columnDefs = [\n",
       "    {\n",
       "        \"targets\": [10, 11],\n",
       "        \"visible\": False\n",
       "    },\n",
       "    {\"className\": \"dt-left\", \"targets\": \"_all\", \"visible\": True},\n",
       "]\n",
       "\n",
       "\n",
       "def highlight_cells(val):\n",
       "    color = 'red' if 'bug' in val else 'blue'\n",
       "    color = 'green' if 'feature-request' in val else color\n",
       "    color = 'purple' if 'debt' in val else color\n",
       "    return f'background-color: {color}'\n",
       "\n",
       "\n",
       "def display_issue_df(df):\n",
       "\n",
       "    df = df.style.map(highlight_cells, subset=['type'])\n",
       "\n",
       "    show(df,\n",
       "        fixedColumns={\"start\": 1, \"end\": 2},\n",
       "        scrollX=True,\n",
       "        search={\"caseInsensitive\": True},\n",
       "        scrollCollapse=True,\n",
       "        scrollY=\"400px\",\n",
       "        paging=False,\n",
       "        buttons=[\"copyHtml5\", \"csvHtml5\", \"excelHtml5\"],\n",
       "        layout={\"topStart\": \"search\", \"topEnd\": \"buttons\"},\n",
       "        select=True\n",
       "    )"
      ]
     },
     {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
       "# Create a dropdown widget with unique labels\n",
       "unique_labels = set(label for labels in df['labels'] for label in labels.split(', '))\n",
       "unique_assignees = set(assignee for assignees in df['assignees'] for assignee in assignees.split(', '))\n",
       "assignees = widgets.Dropdown(options=sorted(unique_assignees), description='Assignee:')\n",
       "assignee_issues = df\n",
       "\n",
       "out = widgets.Output()\n",
       "labels = widgets.SelectMultiple(\n",
       "    options=sorted(list(unique_labels)),\n",
       "    value=sorted(list(unique_labels)),\n",
       "    description='Labels',\n",
       "    disabled=False\n",
       ")\n",
       "\n",
       "def build_labels():\n",
       "    issues = df\n",
       "    lables_list = list(set(sum(issues['labels_list'].tolist(), [])))\n",
       "    labels.options = lables_list\n",
       "    labels.value = lables_list\n",
       "    labels.selected_labels = lables_list\n",
       "\n",
       "def update_pie_chart():\n",
       "    assignee = assignees.value\n",
       "    out.clear_output(wait=True)\n",
       "    global assignee_issues\n",
       "    assignee_issues = df[df['assignees'].isin([assignee])]\n",
       "\n",
       "    assignee_issues = assignee_issues[assignee_issues['labels_list'].apply(lambda x: len(list(set(x) & set(labels.value))) > 0)]\n",
       "    total_rows = assignee_issues.shape[0]\n",
       "    if total_rows == 0:\n",
       "        with out:\n",
       "            print(f\"No issues found for assignee '{assignee}' with labels {labels.value}\")\n",
       "        return\n",
       "\n",
       "    label_counts = assignee_issues['type'].value_counts()\n",
       "\n",
       "    fig = px.pie(\n",
       "        values=label_counts.values,\n",
       "        names=label_counts.index,\n",
       "        color=label_counts.index,\n",
       "        title=f'{total_rows} Issues grouped by types for \"{assignee}\"',\n",
       "        color_discrete_map={'bug':'red',\n",
       "                            'feature-request':'green',\n",
       "                            'debt':'purple',\n",
       "                            'other':'blue'}\n",
       "    )\n",
       "\n",
       "    with out:\n",
       "        display(fig)\n",
       "        display_issue_df(assignee_issues)\n",
       "\n",
       "def dropdown_eventhandler(change):\n",
       "    update_pie_chart()\n",
       "\n",
       "def labels_eventhandler(change):\n",
       "    update_pie_chart()\n",
       "\n",
       "assignees.observe(dropdown_eventhandler, names='value')\n",
       "\n",
       "\n",
       "build_labels()\n",
       "display(assignees)\n",
       "display(out)\n",
       "update_pie_chart()"
      ]
     },
     {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {},
      "outputs": [],
      "source": [
       "issue_types = [\"bug\", \"feature-request\", \"debt\", \"other\"]\n",
       "issue_type_dropdown = widgets.Dropdown(options=sorted(issue_types), description='Type:')\n",
       "filtered_df = assignee_issues\n",
       "\n",
       "out2 = widgets.Output()\n",
       "\n",
       "def update_issues_pie_chart(issue_type):\n",
       "    out2.clear_output(wait=True)\n",
       "    if issue_type == 'other':\n",
       "        filtered_df = assignee_issues[~assignee_issues['type'].str.contains('bug|feature-request|debt')]\n",
       "    else:\n",
       "        filtered_df = assignee_issues[assignee_issues['type'].str.contains(issue_type)]\n",
       "    total_rows = filtered_df.shape[0]\n",
       "    def exclude_types(labels):\n",
       "        if labels is None:\n",
       "            return ''\n",
       "        value = ','.join([label for label in labels.split(', ') if label not in ['bug', 'debt', 'feature-request']])\n",
       "        if value is None or value == '':\n",
       "            return ''\n",
       "        return value\n",
       "\n",
       "    filtered_df.loc[:, 'labels'] = filtered_df['labels'].apply(lambda x: exclude_types(x))\n",
       "    label_counts = filtered_df['labels'].str.split(',').explode().value_counts()\n",
       "    fig = px.pie(values=label_counts.values, names=label_counts.index, title=f'{total_rows} {issue_type} grouped by labels')\n",
       "    with out2:\n",
       "        display(fig)\n",
       "        display_issue_df(filtered_df)\n",
       "\n",
       "\n",
       "def issue_type_dropdown_eventhandler(change):\n",
       "    update_issues_pie_chart(change.new)\n",
       "\n",
       "issue_type_dropdown.observe(issue_type_dropdown_eventhandler, names='value')\n",
       "\n",
       "display(issue_type_dropdown)\n",
       "display(out2)\n",
       "update_issues_pie_chart(issue_type_dropdown.value)"
      ]
     }
    ],
    "metadata": {
     "kernelspec": {
      "display_name": ".venv",
      "language": "python",
      "name": "python3"
     },
     "language_info": {
      "codemirror_mode": {
       "name": "ipython",
       "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.12.4"
     }
    },
    "nbformat": 4,
    "nbformat_minor": 2
   }
